package com.gap.dam.module.business.filemanage;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gap.dam.common.base.PageResultDTO;
import com.gap.dam.common.base.ResponseDTO;
import com.gap.dam.common.constant.JudgeEnum;
import com.gap.dam.module.business.asset.AssetService;
import com.gap.dam.module.business.asset.dao.AssetDao;
import com.gap.dam.module.business.asset.domain.dto.AssetAddDTO;
import com.gap.dam.module.business.asset.domain.dto.AssetUpdateDTO;
import com.gap.dam.module.business.asset.domain.entity.AssetEntity;
import com.gap.dam.module.business.filemanage.dao.AssetFolderDao;
import com.gap.dam.module.business.filemanage.domain.constant.FileManageResponseCodeConst;
import com.gap.dam.module.business.filemanage.domain.constant.FileTypeEnum;
import com.gap.dam.module.business.filemanage.domain.dto.*;
import com.gap.dam.module.business.filemanage.domain.entity.FolderEntity;
import com.gap.dam.module.business.filemanage.domain.vo.FileVO;
import com.gap.dam.module.support.file.constant.FileModuleTypeEnum;
import com.gap.dam.module.support.file.constant.FileResponseCodeConst;
import com.gap.dam.module.support.file.constant.FileServiceTypeEnum;
import com.gap.dam.module.support.file.domain.dto.FileAddDTO;
import com.gap.dam.module.support.file.domain.vo.UploadVO;
import com.gap.dam.module.support.file.service.FileService;
import com.gap.dam.module.support.file.service.IFileService;
import com.gap.dam.module.system.systemconfig.SystemConfigService;
import com.gap.dam.util.BaseEnumUtil;
import com.gap.dam.util.BeanUtil;
import com.gap.dam.util.PageUtil;
import com.gap.dam.util.RequestTokenUtil;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.stream.Collectors;

@Service
public class FileManageService {

    @Autowired
    private AssetFolderDao assetFolderDao;

    @Autowired
    private AssetDao assetDao;

    @Autowired
    private FileService fileService;

    @Autowired
    private AssetService assetService;

    @Autowired
    private SystemConfigService systemConfigService;

    @Autowired
    private java.util.Map<String, IFileService> fileServiceMap;

    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<Long> createNewFolder(FolderAddDTO addDTO) {
        if (addDTO.getParentFolderId() == null) {
            return ResponseDTO.wrap(FileManageResponseCodeConst.PARENT_FOLDER_NOTEXISTS);
        }

        FolderEntity folderEntity = assetFolderDao.selectById(addDTO.getParentFolderId());
        if (folderEntity == null) {
            return ResponseDTO.wrap(FileManageResponseCodeConst.PARENT_FOLDER_NOTEXISTS);
        }

        FolderEntity assetFolderEntity = BeanUtil.copy(addDTO, FolderEntity.class);
        assetFolderDao.insert(assetFolderEntity);

        return ResponseDTO.succData(assetFolderEntity.getId());
    }

    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<String> deleteFile(FileDelDTO delDTO) {
        if (delDTO.getFileType() == FileTypeEnum.FILE.getValue()) {
            AssetEntity assetEntity = assetDao.selectById(delDTO.getFileId());
            if (assetEntity == null) {
                return ResponseDTO.wrap(FileManageResponseCodeConst.FILE_NOT_EXISTS);
            }

            assetEntity.setParentFolderId(-1L);
            assetEntity.setFolder(StringUtils.EMPTY);
            assetEntity.setIsDelete(JudgeEnum.YES.getValue());
            assetDao.updateById(assetEntity);
            return ResponseDTO.succ();
        } else if (delDTO.getFileType() == FileTypeEnum.FOLDER.getValue()) {
            FolderEntity folderEntity = assetFolderDao.selectById(delDTO.getFileId());
            if (folderEntity == null) {
                return ResponseDTO.wrap(FileManageResponseCodeConst.FILE_NOT_EXISTS);
            }

            // 获取文件夹及其子文件夹
            FileQueryDTO folderQueryDTO = new FileQueryDTO();
            // 查询所有文件夹
            List<FolderEntity> folderEntityList = this.queryFolderList(folderQueryDTO);
            List<FolderEntity> folderListWaitDelete = this.findChildren(folderEntity, folderEntityList);
            // 将根文件夹添加进待删除列表
            folderListWaitDelete.add(folderEntity);

            // 删除文件夹下ID
            List<Long> folderIdList = folderListWaitDelete.stream().map(FolderEntity::getId).collect(Collectors.toList());
            assetDao.batchDeleteAsset(folderIdList, JudgeEnum.YES.getValue());

            // 删除文件夹
            assetFolderDao.deleteBatchIds(folderIdList);
            return ResponseDTO.succ();
        } else {
            return ResponseDTO.wrap(FileManageResponseCodeConst.FILE_TYPE_INVALID);
        }
    }

    /**
     * 不分页查询文件夹
     * @param queryDTO
     * @return
     */
    public List<FolderEntity> queryFolderList(FileQueryDTO queryDTO) {
        if (queryDTO == null) {
            queryDTO = new FileQueryDTO();
        }
        return assetFolderDao.queryFolderList(queryDTO);
    }

    /**
     * 递归查询所有子文件夹
     * @param root
     * @param folderEntityList
     */
    public List<FolderEntity> findChildren(FolderEntity root, List<FolderEntity> folderEntityList) {
        List<FolderEntity> children = Lists.newArrayList();
        if (root != null) {
            folderEntityList.forEach(folder -> {
                if (folder.getParentFolderId() != null
                    && folder.getParentFolderId() == root.getId()) {
                    children.add(folder);
                }
            });

            List<FolderEntity> subChildren = Lists.newArrayList();
            children.forEach(folder -> {
                subChildren.addAll(findChildren(folder, folderEntityList));
            });
            children.addAll(subChildren);
        }

        return children;
    }

    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<String> renameFile(FileRenameDTO fileRenameDTO) {
        if (fileRenameDTO.getFileType() == FileTypeEnum.FOLDER.getValue()) {
            FolderEntity folderEntity = assetFolderDao.selectById(fileRenameDTO.getFileId());
            if (folderEntity == null) {
                return ResponseDTO.wrap(FileManageResponseCodeConst.FILE_NOT_EXISTS);
            }

            folderEntity.setFolderName(fileRenameDTO.getFileNewName());
            assetFolderDao.updateById(folderEntity);
        } else if (fileRenameDTO.getFileType() == FileTypeEnum.FILE.getValue()) {
            AssetEntity assetEntity = assetDao.selectById(fileRenameDTO.getFileId());
            if (assetEntity == null)
                return ResponseDTO.wrap(FileManageResponseCodeConst.FILE_NOT_EXISTS);
            assetEntity.setName(fileRenameDTO.getFileNewName());
            assetDao.updateById(assetEntity);
        } else {
            return ResponseDTO.wrap(FileManageResponseCodeConst.FILE_TYPE_INVALID);
        }

        return ResponseDTO.succ();
    }

    /**
     * 移动文件或者文件夹
     * @param fileMoveDTO
     * @return
     */
    public ResponseDTO<String> moveFiles(FileMoveDTO fileMoveDTO) {
        if (fileMoveDTO.getDestFolderId() == null)
            return ResponseDTO.wrap(FileManageResponseCodeConst.FOLDER_INVALID);
        FolderEntity folderEntity = assetFolderDao.selectById(fileMoveDTO.getDestFolderId());
        if (folderEntity == null)
            return ResponseDTO.wrap(FileManageResponseCodeConst.FOLDER_NOT_EXISTS);

        List<Long> filesWaitMove = Lists.newArrayList();
        List<Long> foldersWaitMove = Lists.newArrayList();
        for (FileDTO fileDTO : fileMoveDTO.getSourceFileList()) {
            if (fileDTO.getFileType() == FileTypeEnum.FILE.getValue()) {
                filesWaitMove.add(fileDTO.getId());
            } else if (fileDTO.getFileType() == FileTypeEnum.FOLDER.getValue()) {
                foldersWaitMove.add(fileDTO.getId());
            }
        }

        moveFolders(foldersWaitMove, fileMoveDTO.getDestFolderId());
        moveFiles(filesWaitMove, fileMoveDTO.getDestFolderId());

        return ResponseDTO.succ();
    }

    public void moveFolders(List<Long> foldersId, Long destFolderId) {
        FileUpdateDTO fileUpdateDTO = new FileUpdateDTO();
        fileUpdateDTO.setParentFolderId(destFolderId);
        assetFolderDao.batchUpdate(fileUpdateDTO, foldersId);
    }

    public void moveFiles(List<Long> filesId, Long destFolderId) {
        AssetUpdateDTO updateDTO = new AssetUpdateDTO();
        updateDTO.setParentFolderId(destFolderId);
        assetDao.batchUpdateAsset(filesId, updateDTO);
    }

    /**
     * 分页查询文件列表
     * @param fileQueryDTO
     * @return
     */
    public ResponseDTO<PageResultDTO<FileVO>> queryFileList(FileQueryDTO fileQueryDTO) {
        Page page = PageUtil.convert2QueryPage(fileQueryDTO);
        List<FileVO> fileList = assetFolderDao.queryFileList(page, fileQueryDTO);
        return ResponseDTO.succData(PageUtil.convert2PageResult(page, fileList));
    }

    /**
     * 文件上传
     * @param files
     * @param typeEnum
     * @param moduleType
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<List<UploadVO>> uploadMultifiles(MultipartFile[] files, FileServiceTypeEnum typeEnum,
                                                  Integer moduleType, Long parentFolderId) {
        FileModuleTypeEnum moduleTypeEnum = BaseEnumUtil.getEnumByValue(moduleType, FileModuleTypeEnum.class);
        if (null == moduleTypeEnum) {
            return ResponseDTO.wrap(FileResponseCodeConst.FILE_MODULE_ERROR);
        }
        // 获取文件服务，上传文件
        IFileService fileService = this.getFileService(typeEnum);
        List<UploadVO> uploadVOList = Lists.newArrayList();
        for (MultipartFile file : files) {
            ResponseDTO<UploadVO> responseDTO = fileService.fileUpload(file, moduleTypeEnum.getPath());
            uploadVOList.add(responseDTO.getData());
        }

        if (moduleTypeEnum == FileModuleTypeEnum.ASSET) {
            // Asset需要同时将信息写入Asset表
            List<AssetAddDTO> assetAddDTOList = Lists.newArrayList();
            // 将文件数据保存数据库
            uploadVOList.forEach(uploadVO -> {
                FileAddDTO fileAddDTO = BeanUtil.copy(uploadVO, FileAddDTO.class);
                fileAddDTO.setModuleId("7");
                fileAddDTO.setModuleType(String.valueOf(moduleTypeEnum.getValue()));
                fileAddDTO.setFileLocationType(FileServiceTypeEnum.LOCAL.getValue());
                ResponseDTO<String> responseDTO =  this.fileService.saveFile(fileAddDTO, RequestTokenUtil.getRequestUser());

                AssetAddDTO assetAddDTO = new AssetAddDTO();
                if (StringUtils.isNotEmpty(responseDTO.getData())) {
                    assetAddDTO.setName(uploadVO.getOriginalFileName());
                    if (StringUtils.isNotEmpty(responseDTO.getData())) {
                        assetAddDTO.setFileId(Long.valueOf(responseDTO.getData()));
                    }
                    assetAddDTO.setUrl(uploadVO.getUrl());
                    assetAddDTO.setParentFolderId(parentFolderId);
                    assetAddDTO.setFileSize(String.valueOf(uploadVO.getFileSize()));
//                    assetAddDTOList.add(assetAddDTO);
                    assetService.addAsset(assetAddDTO);
                }
            });
        } else {
            // 将文件数据保存数据库
            uploadVOList.forEach(uploadVO -> {
                FileAddDTO fileAddDTO = BeanUtil.copy(uploadVO, FileAddDTO.class);
                fileAddDTO.setModuleId("7");
                fileAddDTO.setModuleType(String.valueOf(moduleTypeEnum.getValue()));
                fileAddDTO.setFileLocationType(FileServiceTypeEnum.LOCAL.getValue());
                this.fileService.saveFile(fileAddDTO, RequestTokenUtil.getRequestUser());
            });
        }

        return ResponseDTO.succData(uploadVOList);
    }

    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<UploadVO> uploadFile(MultipartFile file, FileServiceTypeEnum typeEnum,
                                                        Integer moduleType, Long parentFolderId) {
        FileModuleTypeEnum moduleTypeEnum = BaseEnumUtil.getEnumByValue(moduleType, FileModuleTypeEnum.class);
        if (null == moduleTypeEnum) {
            return ResponseDTO.wrap(FileResponseCodeConst.FILE_MODULE_ERROR);
        }
        // 获取文件服务，上传文件
        IFileService fileService = this.getFileService(typeEnum);
        ResponseDTO<UploadVO> responseDTO = fileService.fileUpload(file, moduleTypeEnum.getPath());
        UploadVO uploadVO = responseDTO.getData();

        if (moduleTypeEnum == FileModuleTypeEnum.ASSET) {
            // Asset需要同时将信息写入Asset表
            FileAddDTO fileAddDTO = BeanUtil.copy(uploadVO, FileAddDTO.class);
            fileAddDTO.setModuleId("7");
            fileAddDTO.setModuleType(String.valueOf(moduleTypeEnum.getValue()));
            fileAddDTO.setFileLocationType(FileServiceTypeEnum.LOCAL.getValue());
            ResponseDTO<String> saveFileResponseDTO =  this.fileService.saveFile(fileAddDTO, RequestTokenUtil.getRequestUser());

            AssetAddDTO assetAddDTO = new AssetAddDTO();

            if (StringUtils.isNotEmpty(saveFileResponseDTO.getData())) {
                assetAddDTO.setName(uploadVO.getOriginalFileName());
                assetAddDTO.setFileId(Long.valueOf(saveFileResponseDTO.getData()));
                assetAddDTO.setUrl(uploadVO.getUrl());
                assetAddDTO.setParentFolderId(parentFolderId);
                assetAddDTO.setFileSize(String.valueOf(uploadVO.getFileSize()));
                assetService.addAsset(assetAddDTO);
            }
        } else {
            // 将文件数据保存数据库
            FileAddDTO fileAddDTO = BeanUtil.copy(uploadVO, FileAddDTO.class);
            fileAddDTO.setModuleId("7");
            fileAddDTO.setModuleType(String.valueOf(moduleTypeEnum.getValue()));
            fileAddDTO.setFileLocationType(FileServiceTypeEnum.LOCAL.getValue());
            this.fileService.saveFile(fileAddDTO, RequestTokenUtil.getRequestUser());
        }

        return ResponseDTO.succData(uploadVO);
    }

    /**
     * 获取文件服务实现
     *
     * @param typeEnum
     * @return
     */
    private IFileService getFileService(FileServiceTypeEnum typeEnum) {
        /**
         * 获取文件服务
         */
        String serviceName = typeEnum.getServiceName();
        IFileService fileService = fileServiceMap.get(serviceName);
        if (null == fileService) {
            throw new RuntimeException("未找到文件服务实现类：" + serviceName);
        }
        return fileService;
    }

}
